package onvifprotocol

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/beevik/etree"
	"github.com/use-go/onvif/ptz"
	"github.com/use-go/onvif/xsd"
	"github.com/use-go/onvif/xsd/onvif"
	"go.uber.org/zap"
	"iot-base/common/logger"
	"iot-base/common/redis"
	ptz2 "main/onvifprotocol/ptz"
	"time"
)

func (p *CameraDevice) PtzGetServiceCapabilities() {
	req := ptz.GetServiceCapabilities{}
	resp, err := p.device.CallMethod(req)
	if err != nil {
		fmt.Println("resp error!", err)
		return
	}
	tmp, _ := readResponse(resp)
	fmt.Println(tmp)
}

func (p *CameraDevice) PtzGetStatus() {
	req := ptz.GetStatus{}
	resp, err := p.device.CallMethod(req)
	if err != nil {
		fmt.Println("resp error!", err)
		return
	}
	tmp, _ := readResponse(resp)
	fmt.Println(tmp)
}

func (p *CameraDevice) RelativeZoomMove(thingID string, data map[string]interface{}) (map[string]interface{}, error) {
	// 获取云台控制
	resp := make(map[string]interface{})
	pztConfiguration, err := p.PTZConfiguration(thingID, data["ProfileToken"].(string))
	if err != nil {
		return resp, err
	}
	value, ok := pztConfiguration["DefaultRelativeZoomTranslationSpace"]
	zoomSpaces := "http://www.onvif.org/ver10/tptz/PanTiltSpaces/GenericSpeedSpace"
	if ok {
		zoomSpaces = value.(string)
	}
	req := ptz2.RelativeMove{
		ProfileToken: onvif.ReferenceToken(data["ProfileToken"].(string)),
	}
	if z, ok := data["Z"]; ok {
		req.Translation.Zoom.X = z.(float64)
		req.Translation.Zoom.Space = xsd.AnyURI(zoomSpaces)
		req.Speed.Zoom.X = data["Speed"].(float64)
		req.Speed.Zoom.Space = xsd.AnyURI(zoomSpaces)
	}
	result, err := p.CallMethod(req)
	if err != nil {
		fmt.Println("resp error!", err)
		return resp, err
	}
	doc := etree.NewDocument()
	err = doc.ReadFromBytes(result)
	if err != nil {
		return resp, err
	}
	fault := doc.FindElements("./Envelope/Body/Fault/Reason/Text")
	if len(fault) > 0 {
		return resp, errors.New(fault[0].Text())
	}
	return resp, nil
}

// ContinuousMove 连续移动.
func (p *CameraDevice) ContinuousMove(thingID string, data map[string]interface{}) (map[string]interface{}, error) {
	// 获取云台控制
	resp := make(map[string]interface{})
	pztConfiguration, err := p.PTZConfiguration(thingID, data["ProfileToken"].(string))
	if err != nil {
		return resp, err
	}
	move := ptz2.ContinuousMove{
		ProfileToken: onvif.ReferenceToken(data["ProfileToken"].(string)),
	}

	value, ok := pztConfiguration["DefaultContinuousPanTiltVelocitySpace"]
	panTiltSpaces := "http://www.onvif.org/ver10/tptz/PanTiltSpaces/GenericSpeedSpace"
	if ok {
		panTiltSpaces = value.(string)
	}

	if x, ok := data["X"]; ok {
		move.Velocity.PanTilt.X = x.(float64)
		move.Velocity.PanTilt.Space = xsd.AnyURI(panTiltSpaces)
	}
	if y, ok := data["Y"]; ok {
		move.Velocity.PanTilt.Y = y.(float64)
		move.Velocity.PanTilt.Space = xsd.AnyURI(panTiltSpaces)
	}
	//if z, ok := data["Z"]; ok {
	//	move.Velocity.Zoom.X = z.(float64)
	//	move.Velocity.Zoom.Space = xsd.AnyURI("http://www.onvif.org/ver10/tptz/PanTiltSpaces/GenericSpeedSpace")
	//}
	result, err := p.CallMethod(move)
	if err != nil {
		logger.Log.Error("ContinuousMove device.CallMethod err:", zap.Error(err))
		return resp, err
	}
	doc := etree.NewDocument()
	err = doc.ReadFromBytes(result)
	if err != nil {
		return resp, err
	}
	fault := doc.FindElements("./Envelope/Body/Fault/Reason/Text")
	if len(fault) > 0 {
		return resp, errors.New(fault[0].Text())
	}
	return resp, nil
}

func (p *CameraDevice) StopMove(data map[string]interface{}) (map[string]interface{}, error) {
	resp := make(map[string]interface{})
	stop := ptz.Stop{
		ProfileToken: onvif.ReferenceToken(data["ProfileToken"].(string)),
	}
	if _, ok := data["X"]; ok {
		stop.PanTilt = true
	}
	if _, ok := data["Y"]; ok {
		stop.PanTilt = true
	}
	if _, ok := data["Z"]; ok {
		stop.Zoom = true
	}
	result, err := p.CallMethod(stop)
	if err != nil {
		logger.Log.Error("Stop device.CallMethod err:", zap.Error(err))
		return resp, err
	}
	doc := etree.NewDocument()
	err = doc.ReadFromBytes(result)
	if err != nil {
		return resp, err
	}
	fault := doc.FindElements("./Envelope/Body/Fault/Reason/Text")
	if len(fault) > 0 {
		return resp, errors.New(fault[0].Text())
	}
	return resp, nil
}

// GotoPreset 预设位置.
func (p *CameraDevice) GotoPreset(data map[string]interface{}) (map[string]interface{}, error) {
	resp := make(map[string]interface{})
	reqGotoPreset := ptz.GotoPreset{
		ProfileToken: onvif.ReferenceToken(data["ProfileToken"].(string)),
		PresetToken:  onvif.ReferenceToken(data["PresetToken"].(string)),
	}
	result, err := p.CallMethod(reqGotoPreset)
	if err != nil {
		logger.Log.Error("GotoPreset device.CallMethod err:", zap.Error(err))
		return resp, err
	}
	doc := etree.NewDocument()
	err = doc.ReadFromBytes(result)
	if err != nil {
		return resp, err
	}
	fault := doc.FindElements("./Envelope/Body/Fault/Reason/Text")
	if len(fault) > 0 {
		return resp, errors.New(fault[0].Text())
	}
	return resp, nil
}

// GetPresets 获取预设位置.
func (p *CameraDevice) GetPresets(data map[string]interface{}) ([]map[string]interface{}, error) {
	reqGetPresets := ptz.GetPresets{
		ProfileToken: onvif.ReferenceToken(data["ProfileToken"].(string)),
	}
	result, err := p.CallMethod(reqGetPresets)
	if err != nil {
		logger.Log.Error("GetPresets device.CallMethod err:", zap.Error(err))
		return make([]map[string]interface{}, 0), err
	}
	doc := etree.NewDocument()
	err = doc.ReadFromBytes(result)
	if err != nil {
		return make([]map[string]interface{}, 0), err
	}
	fault := doc.FindElements("./Envelope/Body/Fault/Reason/Text")
	if len(fault) > 0 {
		return nil, errors.New(fault[0].Text())
	}
	root := doc.FindElements("./Envelope/Body/GetPresetsResponse/Preset")
	resp := make([]map[string]interface{}, 0, len(root))
	for _, preset := range root {
		temp := make(map[string]interface{})
		temp["PresetToken"] = preset.Attr[0].Value
		temp["PresetName"] = preset.FindElement("Name").Text()
		resp = append(resp, temp)
	}
	return resp, nil
}

// SetPreset 设置预设位置.
func (p *CameraDevice) SetPreset(data map[string]interface{}) (map[string]interface{}, error) {
	resp := make(map[string]interface{})
	reqSetPreset := ptz.SetPreset{
		ProfileToken: onvif.ReferenceToken(data["ProfileToken"].(string)),
		PresetToken:  onvif.ReferenceToken(data["PresetToken"].(string)),
		PresetName:   xsd.String(data["PresetName"].(string)),
	}
	result, err := p.CallMethod(reqSetPreset)
	if err != nil {
		logger.Log.Error("SetPreset device.CallMethod err:", zap.Error(err))
		return resp, err
	}
	doc := etree.NewDocument()
	err = doc.ReadFromBytes(result)
	if err != nil {
		return resp, err
	}
	fault := doc.FindElements("./Envelope/Body/Fault/Reason/Text")
	if len(fault) > 0 {
		return resp, errors.New(fault[0].Text())
	}
	root := doc.FindElement("./Envelope/Body/SetPresetResponse/PresetToken")
	resp["PresetToken"] = root.Text()
	return resp, nil
}

// RemovePreset 删除预设.
func (p *CameraDevice) RemovePreset(data map[string]interface{}) (map[string]interface{}, error) {
	resp := make(map[string]interface{})
	removePreset := ptz.RemovePreset{
		ProfileToken: onvif.ReferenceToken(data["ProfileToken"].(string)),
		PresetToken:  onvif.ReferenceToken(data["PresetToken"].(string)),
	}
	result, err := p.CallMethod(removePreset)
	if err != nil {
		logger.Log.Error("RemovePreset device.CallMethod err:", zap.Error(err))
		return resp, err
	}
	doc := etree.NewDocument()
	err = doc.ReadFromBytes(result)
	if err != nil {
		return resp, err
	}
	fault := doc.FindElements("./Envelope/Body/Fault/Reason/Text")
	if len(fault) > 0 {
		return resp, errors.New(fault[0].Text())
	}
	return resp, nil
}

// PTZConfiguration 获取 ptz 配置.
func (p *CameraDevice) PTZConfiguration(thingID string, profileToken string) (map[string]interface{}, error) {
	key := "ptzConf#" + thingID
	pztStr, _ := redis.Get(key).Result()
	resp := make(map[string]interface{})
	if pztStr != "" {
		err := json.Unmarshal([]byte(pztStr), &resp)
		return resp, err
	}
	result, err := p.GetProfile(profileToken)
	if err != nil {
		return resp, err
	}
	if PTZConfiguration, ok := result["PTZConfiguration"]; ok {
		b, _ := json.Marshal(PTZConfiguration)
		redis.Set(key, b, time.Hour)
		return PTZConfiguration.(map[string]interface{}), nil
	}
	return resp, errors.New("PTZConfiguration does not exist")
}
